package com.auroramanito.uaa.util;

import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.stereotype.Component;

import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.time.Instant;
import java.util.Base64;
import java.util.Optional;


/**
 * 6-1 创建TOTP工具类
 * */
@Slf4j
@RequiredArgsConstructor
@Component
public class TotpUtil {

    private static final long TIME_STEP = 60 *10L;
    private static final int PASSWORD_LENGTH = 6;
    private KeyGenerator keyGenerator;
    private TimeBasedOneTimePasswordGenerator totp;

    /**
     *  初始代码块，这种初始化代码块的执行在构造函数之前
     *  准确说应该是 Java 编译器会吧代码块拷贝到构造函数的最开始。
     * */
    {
        try {


            totp = new TimeBasedOneTimePasswordGenerator(Duration.ofSeconds(TIME_STEP),PASSWORD_LENGTH);
            keyGenerator = KeyGenerator.getInstance(totp.getAlgorithm());
            //SHA-1 and SHA-256需要64字节(512位）的key
            // ;//SHA512需要128字节(1024位）的key
            keyGenerator.init(512);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();

        }
    }

    /**
     * @param time 用于生成 TOTP 的时间
     * @return 一次性验证码
     * @throws InvalidKeyException 非法key抛出异常
     * */
    public String createTotp(Key key, Instant time) throws InvalidKeyException {

        val password = totp.generateOneTimePassword(key,time);
       // 简单的功能，就是把字符串格式化成9位，不足的前面补0。
        val format = "%0" + PASSWORD_LENGTH +"d";
        return String.format(format,password);
    }

    /**
     * 验证 TOTP
     *
     * @param code 要验证的 TOTP
     * @return 是否一致
     * @throws InvalidKeyException 非法 key 抛出异常
     * */
    public boolean verifyTotp(Key key,String code) throws InvalidKeyException{
        val now = Instant.now();
        return  createTotp(key,now).equals(code);
    }

    private Key generateKey(){
        return keyGenerator.generateKey();
    }


    private String encodeKeyToString(Key key){
        return Base64.getEncoder().encodeToString(key.getEncoded());
    }

    /**
     *  直接掉就生成一个加密后的String
     * */
    public String encodeKeyToString(){
        return encodeKeyToString(generateKey());
    }

    public Key decodeKeyFromString(String strKey){
        return new SecretKeySpec(Base64.getDecoder().decode(strKey),totp.getAlgorithm());
    }

    public Optional<String> createTotp(String strKey){
        try {
            return Optional.of(createTotp(decodeKeyFromString(strKey),Instant.now()));
        } catch (InvalidKeyException e) {
            return Optional.empty();
        }
    }

    public long getTimeStepInSecond() {
        return TIME_STEP;
    }

    public Duration getTimeStep() {
        return totp.getTimeStep();
    }
}
